package protomysql

import (
	"strings"

	"gitee.com/crack007/proxy/src/proxy/protocol/protomysql/mysqlfield"

	"gitee.com/crack007/proxy/src/proxy/protocol/protomysql/stmt"

	"gitee.com/crack007/proxy/src/apputil"

	"gitee.com/crack007/proxy/src/constant"
)

var dmlColorMap = map[string]constant.Color{
	"insert": constant.COLOR_GREEN,
	"update": constant.COLOR_YELLO,
	"delete": constant.COLOR_RED,
}

func loadMysqlSTMTExecute(stmtExecute *stmt.MysqlSTMTExecute, mysqlStream *MysqlStream, client *MysqlClient) []string {
	stmtExecute.StatementId = mysqlStream.ReadFixedLengthInteger(4)
	stmtExecute.Flags = mysqlStream.ReadFixedLengthInteger(1)
	stmtExecute.IterationCount = mysqlStream.ReadFixedLengthInteger(4)
	clientQueryAttributes := client.HandshakeResponse41.Capabilities&constant.ClientQueryAttributes == constant.ClientQueryAttributes
	parameterCountAvailable := client.HandshakeResponse41.Capabilities&constant.PARAMETER_COUNT_AVAILABLE == constant.PARAMETER_COUNT_AVAILABLE
	stmtExecute.ParameterCount = client.Stmt.StmtPrepareOk.NumParams
	var params []string
	if client.Stmt.StmtPrepareOk.NumParams > 0 || (clientQueryAttributes && parameterCountAvailable) {
		if clientQueryAttributes {
			stmtExecute.ParameterCount = mysqlStream.ReadLengthEncodedInteger()
		}
		if stmtExecute.ParameterCount > 0 {
			stmtExecute.NullBitmap = mysqlStream.ReadBytes((stmtExecute.ParameterCount + 7) / 8)
			stmtExecute.NewParamsBindFlag = mysqlStream.ReadFixedLengthInteger(1)
		}
		if stmtExecute.NewParamsBindFlag == 1 {
			var fieldHandles []IFieldHandle
			for i := 0; i < stmtExecute.ParameterCount; i++ {
				handle := GetFieldHandle(mysqlStream.ReadFixedLengthInteger(2))
				if handle != nil {
					fieldHandles = append(fieldHandles, handle)
				}
			}
			for _, fieldHandle := range fieldHandles {
				params = append(params, fieldHandle.ProcessBytes(mysqlStream))
			}
		}
	}
	return params
}

func loadMysqlSTMTPrepareOk(prepareOk *stmt.MysqlSTMTPrepareOk, mysqlStream *MysqlStream) {
	prepareOk.StatementId = mysqlStream.ReadFixedLengthInteger(4)
	prepareOk.NumColumns = mysqlStream.ReadFixedLengthInteger(2)
	prepareOk.NumParams = mysqlStream.ReadFixedLengthInteger(2)
	prepareOk.RESERVED1 = mysqlStream.ReadFixedLengthInteger(1)
}

func loadHandshakeResponse41(handshakeResponse41 *mysqlfield.HandshakeResponse41, mysqlStream *MysqlStream) {
	handshakeResponse41.ClientFlag = mysqlStream.ReadFixedLengthInteger(2)
	handshakeResponse41.ExtendClientFlag = mysqlStream.ReadFixedLengthInteger(2)
	handshakeResponse41.MaxPacketSize = mysqlStream.ReadFixedLengthInteger(4)
	handshakeResponse41.CharacterSet = mysqlStream.ReadFixedLengthInteger(1)
	handshakeResponse41.Filler = mysqlStream.ReadBytes(23)
	handshakeResponse41.Username = mysqlStream.ReadNullTerminatedString()
	handshakeResponse41.Capabilities = uint64(handshakeResponse41.ExtendClientFlag<<16 | handshakeResponse41.ClientFlag)
	if handshakeResponse41.Capabilities&constant.ClientPluginAuthLenencClientData == constant.ClientPluginAuthLenencClientData {
		handshakeResponse41.AuthResponse = mysqlStream.ReadLengthEncodedString()
	} else {
		handshakeResponse41.AuthResponseLength = mysqlStream.ReadFixedLengthInteger(1)
		handshakeResponse41.AuthResponse = mysqlStream.ReadFixedLengthString(handshakeResponse41.AuthResponseLength)
	}
	if handshakeResponse41.Capabilities&constant.ClientConnectWithDb == constant.ClientConnectWithDb {
		handshakeResponse41.Database = mysqlStream.ReadNullTerminatedString()
	}
	if handshakeResponse41.Capabilities&constant.ClientPluginAuth == constant.ClientPluginAuth {
		handshakeResponse41.ClientPluginName = mysqlStream.ReadNullTerminatedString()
	}
	if handshakeResponse41.Capabilities&constant.ClientConnectAttrs == constant.ClientConnectAttrs {
		attrs := NewMysqlStream(mysqlStream.ReadBytes(mysqlStream.ReadLengthEncodedInteger()))
		for {
			// 读取name
			attrName := attrs.ReadLengthEncodedString()
			attrValue := attrs.ReadLengthEncodedString()
			handshakeResponse41.ConnectionAttributes = append(handshakeResponse41.ConnectionAttributes, &mysqlfield.ConnectionAttribute{
				Name:  attrName,
				Value: attrValue,
			})
			if attrs.IsEof() {
				break
			}
		}
	}
	handshakeResponse41.IsSsl = (handshakeResponse41.Capabilities & constant.ClientSsl) == constant.ClientSsl
}

func addDmlTag(dml string, sql string) string {
	return apputil.AddColor("["+dml+"] ", dmlColorMap[strings.ToLower(dml)]) + sql
}

func FactoryMysqlProtocol(mysqlPackage *MysqlPackage) IMysqlProtocol {
	cmdType := mysqlPackage.PayloadStream.ReadFixedLengthInteger(1)

	switch cmdType {
	default:
		return nil
	case constant.COM_QUERY:
		return &Query{}
	case constant.COM_STMT_CLOSE:
		return &StmtClose{}
	case constant.COM_STMT_PREPARE:
		return &StmtPrepare{}
	case constant.COM_STMT_EXECUTE:
		return &StmtExecute{}

	}
}

func loadHandshake(mysqlStream *MysqlStream) *mysqlfield.HandshakeV10 {
	handShake := &mysqlfield.HandshakeV10{}
	handShake.Protocol = mysqlStream.ReadFixedLengthInteger(1)
	handShake.Version = mysqlStream.ReadNullTerminatedString()
	handShake.ThreadId = mysqlStream.ReadFixedLengthInteger(4)
	handShake.AuthPluginDataPart1 = mysqlStream.ReadNullTerminatedString()
	handShake.ServerCapabilities = mysqlStream.ReadFixedLengthInteger(2)
	handShake.CharacterSet = mysqlStream.ReadFixedLengthInteger(1)
	handShake.ServerStatus = mysqlStream.ReadFixedLengthInteger(2)
	handShake.ExtendedServerCapabilities = mysqlStream.ReadFixedLengthInteger(2)
	capabilities := uint64(handShake.ExtendedServerCapabilities<<16 | handShake.ServerCapabilities)
	if (capabilities & constant.ClientPluginAuth) == constant.ClientPluginAuth {
		handShake.AuthenticationPluginLength = mysqlStream.ReadFixedLengthInteger(1)
	} else {
		handShake.AuthenticationPluginLength = 0
	}
	handShake.Unused = mysqlStream.ReadBytes(10)
	handShake.AuthPluginDataPart2 = mysqlStream.ReadNullTerminatedString()
	if (capabilities & constant.ClientPluginAuth) == constant.ClientPluginAuth {
		handShake.AuthPluginName = mysqlStream.ReadNullTerminatedString()
	}
	return handShake
}
